Skip to content

Resolve .localhost to loopback per Fetch spec#4456

Closed
FelixVaughan wants to merge 4 commits intonodejs:mainfrom
FelixVaughan:fix/loopback-resolve
Closed

Resolve .localhost to loopback per Fetch spec#4456
FelixVaughan wants to merge 4 commits intonodejs:mainfrom
FelixVaughan:fix/loopback-resolve

Conversation

@FelixVaughan
Copy link
Copy Markdown
Contributor

This relates to...

#4391

Rationale

Align localhost public-suffix resolution with the Fetch spec (Resolving domains -> “resolve an origin”): any localhost or *.localhost host resolves to loopback.
Spec: Fetch 2.5 resolve an origin

Changes/Fixes

  • Add isLocalhostPublicSuffix(hostname)
  • Inject custom lookup (localhostLookup) for both net and TLS paths to short‑circuit DNS for .localhost
  • No change for non‑localhost hosts or literal IPs

Status

Comment thread lib/core/connect.js
}

// Implements the relevant part of "resolve an origin" for localhost public suffix
// https://fetch.spec.whatwg.org/#resolve-an-origin
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fetch specific code and should not be in core

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll find a new home for it. Any suggestions?

Comment thread lib/core/connect.js
}

// Exact localhost or any subdomain of .localhost
return h === 'localhost' || h.endsWith('.localhost')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure this can be more performant implemented

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see what I can do

Comment thread lib/core/connect.js
const ipv4 = '127.0.0.1'
const ipv6 = '::1'

const results = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you instantiate this array on every call?

Copy link
Copy Markdown
Contributor Author

@FelixVaughan FelixVaughan Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could hoist out of the function but I think that would sacrifice readability for minor optimizations

Comment thread lib/core/connect.js
}
}

// Implements the relevant part of "resolve an origin" for localhost public suffix
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the relevant part? What parts are messing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the specs there are 4 different cases for origin resolution; only one has to do with localhost

If origin’s host’s public suffix is "localhost" or "localhost.", then return « ::1, 127.0.0.1 ».

Thats the one this addresses

Comment thread lib/core/connect.js

// Spec: If public suffix is localhost, resolve to loopback
if (isLocalhostPublicSuffix(hostname)) {
tlsOptions.lookup = localhostLookup
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have to check what this lookup attribute is doing

Copy link
Copy Markdown
Contributor Author

@FelixVaughan FelixVaughan Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its purpose is to resolve a hostname. By default it performs a dns lookup but here we override it to short-circuit the lookup. Here's a link to the docs

@Uzlopak
Copy link
Copy Markdown
Contributor

Uzlopak commented Aug 24, 2025

@KhafraDev

Copy link
Copy Markdown
Member

@KhafraDev KhafraDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't "per Fetch spec". The implementation is noticeably different.

@FelixVaughan
Copy link
Copy Markdown
Contributor Author

This isn't "per Fetch spec". The implementation is noticeably different.

@KhafraDev could point me in the right direction?

@KhafraDev
Copy link
Copy Markdown
Member

There is nothing I can say here that wasn't already said in the issue.

3 possibilities:

  • Implement as an interceptor
  • Implement in node core
  • For fetch only, update httpNetworkFetch to match the spec (easier said than done)

This also has other issues like overriding tlsoptions.lookup which iirc can be set by users and following the fetch spec for some reason. You link this but none of the steps are actually implemented, and even if they were, undici hardly seems like the correct place to implement this. As mentioned in the issue, given the RFC, it seems like something that should be fixed in node core (it seems like cares does it already?).

@FelixVaughan
Copy link
Copy Markdown
Contributor Author

Good to know I wasn't on the right path. I agree that ideally the main fix should be in core and I could open an issue there but I wanted to see if there's anything patchable here. I would be interested in hearing more about the httpNetworkFetch approach as well

Also not sure what cares means in this context

@Uzlopak
Copy link
Copy Markdown
Contributor

Uzlopak commented Aug 28, 2025

@KhafraDev
Copy link
Copy Markdown
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants